
/**
MIT License

Copyright (c) 2009 Tim Kelsey 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@author Tim Kelsey <tim.callidus@gmail.com>
*/

package callidus.bits.GenericProvider;

import java.util.HashMap;
import java.util.Vector;

import android.util.Log;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;

import org.xmlpull.v1.XmlPullParser;


// TODO: 1 - this is all a bit fragile, it needs proper update support and more
// fields adding to the DbCOlumn definition. I may also not keep the parsed schema
// around in memory if i can just use the created DB meta data.

/**
 * This class helps open, create, and upgrade the database file.
 */
public class GenericDatabase extends SQLiteOpenHelper {

	/**
	 * simple POD to hold definition data for a single column
	 */
	private class DataColumn{
		public String				mName;			// column name
		public String				mType;			// column type 
		// more to come ...
	}
	
	/**
	 * a collection of data columns form a data set
	 */
	private class DataTable {
		public String				mName;			// set name
		public Vector<DataColumn>	mColumns;		// set columns
	}
	
	
	private Vector<DataTable>		mDataTables;	// collection of data sets
	private Context				mCtx;			// context
	private int					mResId;			// resource id
	private HashMap 				mMimeMap;		// mime strings

	public Vector<DataTable> getTables(){
		return mDataTables;
	}
	
	private void parse() { 
		try {
			XmlPullParser xpp = mCtx.getResources().getXml( mResId );
			int eventType = xpp.getEventType();
			while( eventType != XmlPullParser.END_DOCUMENT ) {
				switch( eventType ) {
				
				case XmlPullParser.START_DOCUMENT:
					processStartDocument( xpp );
					break;
				
					
				case XmlPullParser.END_DOCUMENT:
					processEndDocument( xpp );
					break;
					
					
				case XmlPullParser.START_TAG:
					processStartTag( xpp );
					break;
					
					
				case XmlPullParser.END_TAG:
					processEndTag( xpp );
					break;
					
					
				case XmlPullParser.TEXT:
					processText( xpp );
					break;
				
				}
				eventType = xpp.next();
			}
		}
		catch( Exception e ){
			// TODO: handle exceptions
		}
	}
	
	private void processStartDocument( XmlPullParser xpp ){
	}
	
	
	private void processEndDocument( XmlPullParser xpp ){		
	}
	
	
	private void processStartTag( XmlPullParser xpp ){
		
		// new database, set it up
		if( xpp.getName().equals( "database" ) ){
			mDataTables = new Vector<DataTable>();
		}
		
		// new table
		else if( xpp.getName().equals( "table" ) ){
			DataTable tab = new DataTable();
			tab.mName = xpp.getAttributeValue(0);
			tab.mColumns = new Vector<DataColumn>();
			mDataTables.add( tab );
		}
		
		// new column
		else if( xpp.getName().equals( "column" ) ){
			DataColumn col = new DataColumn();
			int count = xpp.getAttributeCount();
			for( int i = 0; i < count; ++i ){
				if ( xpp.getAttributeName(i).equals( "name" ) ) {
					col.mName = xpp.getAttributeValue(i);
				} else if ( xpp.getAttributeName(i).equals( "type" ) ) {
					col.mType = xpp.getAttributeValue(i);
				}
				// more to come ...
			}
			mDataTables.lastElement().mColumns.add( col );
		}
		
		// mime info block
		else if( xpp.getName().equals( "mimes" ) ){
			mMimeMap = new HashMap();
		}
		
		// mime string
		else if( xpp.getName().equals( "mime" ) ){
			String name = null;
			String type = null;
			if( xpp.getAttributeCount() == 2 ){
				if( xpp.getAttributeName( 0 ).equals( "table" ) ){
					name = xpp.getAttributeValue( 0 );
				}
				
				if( xpp.getAttributeName( 1 ).equals( "type" ) ) {
					type = xpp.getAttributeValue( 1 );
				}
			}
			
			if( name != null && type != null ){
				mMimeMap.put( name, type );
				Log.println(Log.INFO, "dbHelper", "mime noted: " + name + " : " + type + "\n" );
			}
		}
		
		
	}
	
	
	private void processEndTag( XmlPullParser xpp ){	
		// room for some basic data validation here ....
	}
	
	
	private void processText( XmlPullParser xpp ){	
		// room for actual value parsing here ...
	}
	
	
	
	
	
	
	
	
	
	public GenericDatabase(Context context, String name,  CursorFactory fact, int version, int resId) {
		super( context, name, null, 1);
		mCtx = context; // dont want to store this, im sure the base class must ?
		mResId = resId;
	}

	@Override
	public void onOpen(SQLiteDatabase db) {
		if( mDataTables == null ){
			parse();
		}
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		
		// get data 
		if( mDataTables == null ){
			parse();
		}			
		
		// now build the DB
		for( int i=0; i < mDataTables.size(); ++i ){
			String qry = "CREATE TABLE " + mDataTables.elementAt(i).mName + "( ";
			for( int j=0; j < mDataTables.elementAt(i).mColumns.size(); ++j ) {
				qry += mDataTables.elementAt(i).mColumns.elementAt(j).mName + " ";
				qry += mDataTables.elementAt(i).mColumns.elementAt(j).mType + ", ";
			}
			
			int len = qry.length();
			qry = qry.substring( 0, len-2 );
			qry += ")";
			Log.println(Log.INFO, "dbHelper", qry + "\n" );
			db.execSQL( qry );
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO: we dont know the table name, so drop them all ....
	}
	
	
	
	
	
	public String getMime( String key ){
		Object tmp = mMimeMap.get( key );
		if( tmp == null ) {
			throw new IllegalArgumentException( "No mime info for '" + key + "'" );
		}
		String str = String.valueOf( tmp ); 
		return str;
	}
	
}






